package com.lsh.oms.core.service.payment;

import com.alibaba.fastjson.JSONArray;
import com.lsh.base.lang.json.Jsons;
import com.lsh.oms.core.constant.ReturnCode;
import com.lsh.oms.core.dao.other.OrderShippingHeadMapper;
import com.lsh.oms.core.dao.other.OrderSignHeadMapper;
import com.lsh.oms.core.dao.payment.BillPaymentInfoDao;
import com.lsh.oms.core.exception.BusinessException;
import com.lsh.oms.core.model.order.OrderHead;
import com.lsh.oms.core.model.order.OrderShippingHeadWithBLOBs;
import com.lsh.oms.core.model.order.OrderSignHead;
import com.lsh.oms.core.model.payment.BillPaymentInfo;
import com.lsh.oms.core.proxy.service.PaymentProxy;
import com.lsh.oms.core.service.order.OrderQueryService;
import com.lsh.oms.core.service.payment.core.PaymentService;
import com.lsh.oms.core.service.tool.Criteria;
import com.lsh.oms.core.service.tool.Pageable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.lsh.base.lang.bean.Beans.getInt;

@Component("payment.service")
public class PaymentServiceImpl implements PaymentService {

    @Resource
    private BillPaymentInfoDao mapper;

    @Resource
    private OrderSignHeadMapper receiptMapper;

    @Resource
    private OrderShippingHeadMapper shippingMapper;

    @Resource
    private OrderQueryService orderQueryService;

    @Resource
    private PaymentProxy paymentProxy;

    private static final Logger LOGGER = LoggerFactory.getLogger(PaymentServiceImpl.class);

    @Override
    public Pageable<BillPaymentInfo> query(Criteria criteria, Long page, Long pageLimit) {
        return new Pageable<>(findPayments(criteria, page, pageLimit), page);
    }

    private List<BillPaymentInfo> findPayments(Criteria criteria, Long page, Long pageLimit) {
        OrderHead order = findOrder(criteria);
        if (order.hasPaymentNo()) {
            if (!order.paid()) {
                return Collections.emptyList();
            }
            enhanceCriteria(criteria, order);
            return mapper.findBaseOnCriteria(criteria, ((page - 1) * pageLimit), pageLimit);
        } else {
            return findPaymentInPaymentService(order);
        }
    }

    private void enhanceCriteria(Criteria criteria, OrderHead order) {
        if (order.hasParentOrder()) {
            criteria.replace("order-id", order.getParentOrderCode().toString());
        }
    }

    private List<BillPaymentInfo> findPaymentInPaymentService(OrderHead order) {
        OrderSignHead receiptOrder = findReceiptOrder(order.getOrderCode());
        List<Map<String, Object>> payments = new ArrayList<>();
        if (receiptOrder.combinedPayment()) {
            Object paymentNo = receiptOrder.extendedProperties().get("pay_payment_no");
            LOGGER.info("payment no: {}, class: {}", paymentNo, paymentNo.getClass());
            if (paymentNo instanceof String) {
                if (((String) paymentNo).startsWith("[")) {
                    JSONArray.parseArray(((String) paymentNo), String.class).forEach($ -> payments.add(paymentProxy.queryPayInfo(null, (String) $, receiptOrder.getVenderId())));
                } else {
                    payments.add(paymentProxy.queryPayInfo(null, (String) paymentNo, receiptOrder.getVenderId()));
                }
            } else {
                ((JSONArray) paymentNo).forEach($ -> payments.add(paymentProxy.queryPayInfo(null, (String) $, receiptOrder.getVenderId())));
            }
        } else {
            Map<String, Object> payment = queryPaymentByReceipt(receiptOrder);
            // for some previous receipt order has no pay_payment_no in ext
            payment = (payment != null) ? payment : queryPaymentByShipping(order, receiptOrder);
            payments.add(payment);
        }
        LOGGER.info("payments: {}", Jsons.toJsonString(payments));
        List<BillPaymentInfo> result = payments.stream().filter($ -> ($ != null) && getInt($, "ret") == 0).map($ -> {
            BillPaymentInfo payment = new BillPaymentInfo();
            payment.setVenderId(receiptOrder.getVenderId());
            payment.setPayTradeId((String) $.get("tradeId"));
            payment.setBillCode(order.getOrderCode());
            payment.setPayChannelType((Integer) $.get("payChannelType"));
            payment.setPayAmount(new BigDecimal(String.valueOf($.get("requestAmount"))));
            payment.setPayWay((String) $.get("payWay"));
            payment.setPayStatus((Integer) $.get("payStatus"));
            Long payTime = (Long) $.get("payTime");
            if (payTime != null) {
                payment.setPayTime(new Date(payTime));
            }
            payment.setCreateTime(new Date((Long) $.get("createdAt")));
            payment.setExt((String) $.get("ext"));
            payment.setIsValid(1);
            return payment;
        }).collect(Collectors.toList());

        if (result.isEmpty()) {
            throw new BusinessException(ReturnCode.REQUEST_FAIL, "CAN NOT FIND PAYMENT IN PAYMENT SERVICE");
        }
        return result;
    }

    private Map<String, Object> queryPaymentByReceipt(OrderSignHead receiptOrder) {
        String tradeId = String.valueOf(receiptOrder.getReceiptOrderId());
        return paymentProxy.queryPayInfo(tradeId, null, receiptOrder.getVenderId());
    }

    private Map<String, Object> queryPaymentByShipping(OrderHead order, OrderSignHead receiptOrder) {
        OrderShippingHeadWithBLOBs shipping = shippingMapper.selectByshippingOrderId(receiptOrder.getShippingOrderId());
        if (shipping == null) {
            LOGGER.warn("can not find shipping by shipping id: {}", receiptOrder.getShippingOrderId());
            return null;
        }
        String tradeId = shipping.getWaybillNo() + "_" + (order.hasParentOrder() ? order.getParentOrderCode() : order.getOrderCode());
        return paymentProxy.queryPayInfo(tradeId, null, receiptOrder.getVenderId());
    }

    private OrderSignHead findReceiptOrder(Long orderId) {
        OrderSignHead receiptOrder = receiptMapper.selectByOrderCode(orderId);
        if (receiptOrder == null) {
            throw new BusinessException(ReturnCode.REQUEST_FAIL, "CAN NOT FIND RECEIPT ORDER");
        }
        return receiptOrder;
    }

    private OrderHead findOrder(Criteria criteria) {
        String orderId = criteria.getFirst("order-id");
        if (orderId == null) {
            String receiptOrderId = criteria.getFirst("receipt-order-id");
            if (receiptOrderId == null) {
                throw new BusinessException(ReturnCode.REQUEST_FAIL, "QUERY PARAMETER MISSING");
            }
            OrderSignHead receipt = receiptMapper.selectByReceiptOrderId(Long.valueOf(receiptOrderId));
            orderId = receipt.getOrderId().toString();
        }
        OrderHead order = orderQueryService.findByCode(Long.valueOf(orderId), false, false);
        if (order == null) {
            throw new BusinessException(ReturnCode.REQUEST_FAIL, "CAN NOT FIND ORDER");
        }
        return order;
    }
}
